package org.hepeng.workx.spring.cloud.netflix.eureka.server;

import org.apache.commons.collections.MapUtils;
import org.hepeng.workx.constant.ExecuteMode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceCanceledEvent;
import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceRegisteredEvent;
import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceRenewedEvent;
import org.springframework.cloud.netflix.eureka.server.event.EurekaRegistryAvailableEvent;
import org.springframework.cloud.netflix.eureka.server.event.EurekaServerStartedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.Bean;
import org.springframework.util.ClassUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;


/**
 * @author he peng
 */

@ConditionalOnClass(EnableEurekaServer.class)
public class EurekaServerEventListenerConfiguration {

    @Autowired(required = false)
    private Map<String , EurekaServerEventConsumer<? extends EurekaServerStartedEvent>> startedEventConsumers;

    @Autowired(required = false)
    private Map<String , EurekaServerEventConsumer<? extends EurekaRegistryAvailableEvent>> registryAvailableEventConsumers;

    @Autowired(required = false)
    private Map<String , EurekaServerEventConsumer<? extends EurekaInstanceRenewedEvent>> renewedEventConsumers;

    @Autowired(required = false)
    private Map<String , EurekaServerEventConsumer<? extends EurekaInstanceRegisteredEvent>> registeredEventConsumers;

    @Autowired(required = false)
    private Map<String , EurekaServerEventConsumer<? extends EurekaInstanceCanceledEvent>> canceledEventConsumers;

    @Bean
    public EurekaServerEventListener eurekaServerEventListener(ApplicationContext applicationContext) {
        EurekaEventConsumerExecutor executor = getExecutor(applicationContext);
        EurekaServerEventListener listener = new EurekaServerEventListener(executor);
        registerEventConsumer(listener , EurekaServerStartedEvent.class , this.startedEventConsumers);
        registerEventConsumer(listener , EurekaRegistryAvailableEvent.class , this.registryAvailableEventConsumers);
        registerEventConsumer(listener , EurekaInstanceRenewedEvent.class , this.renewedEventConsumers);
        registerEventConsumer(listener , EurekaInstanceRegisteredEvent.class , this.registeredEventConsumers);
        registerEventConsumer(listener , EurekaInstanceCanceledEvent.class , this.canceledEventConsumers);
        return listener;
    }

    @Bean
    public EurekaServerEventConsumer<EurekaServerStartedEvent> eurekaServerEventConsumer() {
        return new TestEurekaServerEventConsumer();
    }

    private void registerEventConsumer(EurekaServerEventListener listener , Class<? extends ApplicationEvent> key , Map eventConsumers) {
        if (MapUtils.isNotEmpty(eventConsumers)) {
            List consumers = new ArrayList(eventConsumers.values());
            listener.registerEventConsumer(key , consumers);
        }
    }

    private EurekaEventConsumerExecutor getExecutor(ApplicationContext context) {
        EurekaEventConsumerExecutor executor = new SyncEurekaEventConsumerExecutor();
        Map<String, Object> beans = context.getBeansWithAnnotation(EnableEurekaServerEventListener.class);
        if (MapUtils.isNotEmpty(beans)) {
            Object obj = new Object();
            for (Map.Entry<String, Object> entry : beans.entrySet()) {
                obj = entry.getValue();
                break;
            }

            EnableEurekaServerEventListener annotation;
            if (ClassUtils.isCglibProxy(obj)) {
                annotation = obj.getClass().getSuperclass().getAnnotation(EnableEurekaServerEventListener.class);
            } else {
                annotation = obj.getClass().getAnnotation(EnableEurekaServerEventListener.class);
            }

            if (Objects.nonNull(annotation)) {
                ExecuteMode executeMode = annotation.executeMode();
                if (Objects.equals(executeMode, ExecuteMode.ASYNC)) {
                    executor = new AsyncEurekaEventConsumerExecutor();
                }
            }
        }
        return executor;
    }
}
